// Cell names have been changed in this file by netl_namemap on Mon Jan  3 04:00:14 UTC 2022
////////////////////////////////////////////////////////////////////////////// 
//
//  pcs_raw_tx_ctl.v
//
//  TX control, handles interaction between TX lane and MPLLs.
//
//  Original Author: Ameer Youssef 
//  Current Owner: Ameer Youssef   
//
////////////////////////////////////////////////////////////////////////////// 
//
// Copyright (C) 2013 Synopsys, Inc.  All rights reserved.
//
// SYNOPSYS CONFIDENTIAL - This is an unpublished, proprietary work of
// Synopsys, Inc., and is fully protected under copyright and trade secret
// laws.  You may not view, use, disclose, copy, or distribute this file or
// any information contained herein except pursuant to a valid written
// license agreement. It may not be used, reproduced, or disclosed to others
// except in accordance with the terms and conditions of that agreement.
//
////////////////////////////////////////////////////////////////////////////// 
//
//    Perforce Information
//    $Author: yilin $
//    $File: //dwh/up16/main/dev/pcs_raw/dig/rtl/pcs_raw_tx_ctl.v $
//    $DateTime: 2016/03/09 11:36:56 $
//    $Revision: #24 $
//
////////////////////////////////////////////////////////////////////////////// 

`include "dwc_e12mp_phy_x4_ns_pcs_raw_macros.v"

`timescale 1ns/10fs
module dwc_e12mp_phy_x4_ns_pcs_raw_tx_ctl (
// Clocks and resets
input  wire          cr_clk,
input  wire          cr_rst,
input  wire [2:0]    ref_range,

// PCS TX interface
input  wire          tx_clk_i,
input  wire          tx_reset_i,
input  wire          tx_req_i,
input  wire [1:0]    tx_pstate_i,
input  wire          tx_lpd_i,
input  wire [1:0]    tx_width_i,
input  wire [2:0]    tx_rate_i,
input  wire          tx_mpllb_sel_i,
input  wire          tx_mpll_en_i,
input  wire          tx_master_mplla_state_i,
input  wire          tx_master_mpllb_state_i,
input  wire          tx_detrx_req_i,
output reg           tx_ack_i,
output reg           tx_detrx_result_i,

// PMA SUP interface
input  wire          mplla_en_i,
input  wire          mpllb_en_i,
input  wire          mplla_state_i,
input  wire          mpllb_state_i,

// PMA MPLLA clocks
input  wire          mplla_word_clk_i,
input  wire          mplla_dword_clk_i,
input  wire          mplla_qword_clk_i,
input  wire          mplla_oword_clk_i,
input  wire          mplla_div66_clk_i,
input  wire          mplla_div33_clk_i,
input  wire          mplla_div16p5_clk_i,
input  wire          mplla_div_clk_i,

// PMA MPLLB clocks
input  wire          mpllb_word_clk_i,
input  wire          mpllb_dword_clk_i,
input  wire          mpllb_qword_clk_i,
input  wire          mpllb_oword_clk_i,
input  wire          mpllb_div_clk_i,

// PMA RX clock
input  wire          rx_clk_i,

// PMA TX interface
output wire          tx_pma_clk_i,
output wire          tx_pma_reset_i,
output reg           tx_pma_req_i,
output wire [1:0]    tx_pma_pstate_i,
output wire          tx_pma_lpd_i,
output wire [1:0]    tx_pma_width_i,
output wire [2:0]    tx_pma_rate_i,
output wire          tx_pma_mpllb_sel_i,
output reg           tx_pma_detrx_req_i,
input  wire          tx_pma_ack_i,
input  wire          tx_pma_detrx_result_i,

// Internal from/to other Raw PCS lanes (pcs_raw_lane)
input  wire          lane_mplla_en_in_i,
input  wire          lane_mpllb_en_in_i,
output wire          lane_mplla_en_out_i,
output wire          lane_mpllb_en_out_i,

// CREG interface (accessing CREGs in this block)                     
output wire [15:0]   cr_rd_data,
input  wire [15:0]   cr_wr_data,
input  wire          cr_wr_en,
input  wire [1:0]    cr_sel,

input  wire          scan_mode_i,
input  wire          scan_shift_cg_i,
input  wire          scan_set_rst_i
);

// -------------------------
// Registers and nets
// -------------------------

wire       cr_tx_rst;
wire       tx_req_crclk;
wire       tx_detrx_req_crclk;
wire       tx_pma_detrx_result_nxt;
wire       tx_pma_ack_crclk;
wire       master_mplla_state_crclk;
wire       master_mpllb_state_crclk;
wire       mplla_state_crclk;
wire       mpllb_state_crclk;
wire       new_req;
wire [1:0] req_tx_pstate_nxt;
wire       req_tx_lpd_nxt;
wire [1:0] req_tx_width_nxt;
wire [2:0] req_tx_rate_nxt;
wire       req_tx_mpllb_sel_nxt;
wire       req_tx_mpll_en_nxt;
wire       req_rate_change;
reg  [1:0] req_tx_pstate;
reg        req_tx_lpd;
reg  [1:0] req_tx_width;
reg  [2:0] req_tx_rate;
reg        req_tx_mpllb_sel;
reg        req_tx_mpll_en;
reg        tx_mplla_en;
reg        tx_mpllb_en;
reg  [2:0] tx_ctl_state;
reg        tx_ctl_reset;
reg  [8:0] tx_wait_cnt;
reg  [1:0] curr_tx_pstate;
reg        curr_tx_lpd;
reg  [1:0] curr_tx_width;
reg  [2:0] curr_tx_rate;
reg        curr_tx_mpllb_sel;
reg        curr_tx_mpll_en;

reg        tx_allow_rxdet;
wire       tx_p0_allow_rxdet_r;
wire       tx_p0s_allow_rxdet_r;
wire       tx_p1_allow_rxdet_r;
wire       tx_p2_allow_rxdet_r;

wire       tx_wait_cnt_ne_0;
wire [8:0] tx_wait_cnt_dec;

wire [5:0] tx_wait_mpll_off_time_r;
reg  [7:0] tx_wait_mpll_off_cr_cyc;
                 
wire [3:0] tx_clk_sel_r;
wire       tx_clk_en_r;

// Add TX Reset
// Mantis 7223 Remove ATPG coverage flop for cascaded pcs_raw_gen_rst_sync
dwc_e12mp_phy_x4_ns_pcs_raw_gen_rst_sync #(.ATPG_COV(0)) cr_tx_rst_sync (
  .sync_rst        (cr_tx_rst),
  .clk             (cr_clk),
  .scan_mode_i     (scan_mode_i),
  .scan_set_rst_i  (scan_set_rst_i),
  .async_rst       (cr_rst | tx_reset_i )
);

// Set PMA tx reset
assign tx_pma_reset_i = cr_tx_rst;

// Synchronize PCS TX request to cr_clk domain
dwc_e12mp_phy_x4_ns_gen_sync tx_req_sync (
  .q   (tx_req_crclk),
  .clk (cr_clk),
  .rst (cr_tx_rst),
  .d   (tx_req_i)
);

// Synchronize PCS TX detrx request to cr_clk domain
dwc_e12mp_phy_x4_ns_gen_sync tx_detrx_req_sync (
  .q   (tx_detrx_req_crclk),
  .clk (cr_clk),
  .rst (cr_tx_rst),
  .d   (tx_detrx_req_i)
);

// Synchronize PMA TX ack to cr_clk domain
// (needs to be asserted from the beginning)
dwc_e12mp_phy_x4_ns_gen_sync #(.RST_VAL(1)) tx_pma_ack_sync (
  .q   (tx_pma_ack_crclk),
  .clk (cr_clk),
  .rst (cr_tx_rst),
  .d   (tx_pma_ack_i)
);

// Synchronize tx_master_mplla_state_i to cr_clk domain
dwc_e12mp_phy_x4_ns_gen_sync master_mplla_state_sync (
  .q   (master_mplla_state_crclk),
  .clk (cr_clk),
  .rst (cr_tx_rst),
  .d   (tx_master_mplla_state_i)
);

// Synchronize tx_master_mpllb_state_i to cr_clk domain
dwc_e12mp_phy_x4_ns_gen_sync master_mpllb_state_sync (
  .q   (master_mpllb_state_crclk),
  .clk (cr_clk),
  .rst (cr_tx_rst),
  .d   (tx_master_mpllb_state_i)
);

// Synchronize mplla_state_i to cr_clk domain
dwc_e12mp_phy_x4_ns_gen_sync mplla_state_sync (
  .q   (mplla_state_crclk),
  .clk (cr_clk),
  .rst (cr_tx_rst),
  .d   (mplla_state_i)
);

// Synchronize mpllb_state_i to cr_clk domain
dwc_e12mp_phy_x4_ns_gen_sync mpllb_state_sync (
  .q   (mpllb_state_crclk),
  .clk (cr_clk),
  .rst (cr_tx_rst),
  .d   (mpllb_state_i)
);

// if received a request, or out of reset and pma ack has deasserted
assign new_req = tx_req_crclk | (tx_ctl_reset & ~tx_pma_ack_crclk);

// Rate change request
assign req_rate_change = (req_tx_mpllb_sel_nxt != curr_tx_mpllb_sel) | (req_tx_rate_nxt != curr_tx_rate);

// Hand instantiated mux on async tx_pstate_i input
dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(2)) tx_pstate_mux (
  .out (req_tx_pstate_nxt),
  .sel (new_req),
  .d0  (req_tx_pstate),
  .d1  (tx_pstate_i)
);

// Hand instantiated mux on async tx_lpd_i input
dwc_e12mp_phy_x4_ns_gen_mux tx_lpd_mux (
  .out (req_tx_lpd_nxt),
  .sel (new_req),
  .d0  (req_tx_lpd),
  .d1  (tx_lpd_i)
);

// Hand instantiated mux on async tx_width_i input
dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(2)) tx_width_mux (
  .out (req_tx_width_nxt),
  .sel (new_req),
  .d0  (req_tx_width),
  .d1  (tx_width_i)
);

// Hand instantiated mux on async tx_rate_i input
dwc_e12mp_phy_x4_ns_gen_mux #(.WIDTH(3)) tx_rate_mux (
  .out (req_tx_rate_nxt),
  .sel (new_req),
  .d0  (req_tx_rate),
  .d1  (tx_rate_i)
);

// Hand instantiated mux on async tx_mpllb_sel_i input
dwc_e12mp_phy_x4_ns_gen_mux tx_mpllb_sel_mux (
  .out (req_tx_mpllb_sel_nxt),
  .sel (new_req),
  .d0  (req_tx_mpllb_sel),
  .d1  (tx_mpllb_sel_i)
);

// Hand instantiated mux on async tx_mpll_en_i input
dwc_e12mp_phy_x4_ns_gen_mux req_tx_mpll_en_mux (
  .out (req_tx_mpll_en_nxt),
  .sel (new_req),
  .d0  (req_tx_mpll_en),
  .d1  (tx_mpll_en_i)
);

// Hand instantiated mux on async tx_pma_detrx_result_i input
// (update result on rising edge of tx_pma_ack when receiving detrx request)
dwc_e12mp_phy_x4_ns_gen_mux tx_pma_detrx_result_mux (
  .out (tx_pma_detrx_result_nxt),
  .sel (tx_pma_detrx_req_i & tx_pma_ack_crclk),
  .d0  (tx_detrx_result_i),
  .d1  (tx_pma_detrx_result_i)
);

// Set lane_mplla/b_en_out:
// OR local tx_mplla/b_en with input lane_mplla/b_en_in
assign lane_mplla_en_out_i = tx_mplla_en | lane_mplla_en_in_i;
assign lane_mpllb_en_out_i = tx_mpllb_en | lane_mpllb_en_in_i;

// Set rate, lpd, pstate and mpllb_sel inputs to PMA
assign tx_pma_pstate_i    = req_tx_pstate;
assign tx_pma_lpd_i       = req_tx_lpd;
assign tx_pma_width_i     = req_tx_width;
assign tx_pma_rate_i      = req_tx_rate;
assign tx_pma_mpllb_sel_i = req_tx_mpllb_sel;

// Wait counter
assign tx_wait_cnt_ne_0 = |tx_wait_cnt;
assign tx_wait_cnt_dec = tx_wait_cnt - 9'd1;

// Power state mappings
localparam PSTATE_P0  = 2'd0;
localparam PSTATE_P0S = 2'd1;
localparam PSTATE_P1  = 2'd2;
localparam PSTATE_P2  = 2'd3;

always @* begin
  case (curr_tx_pstate)
    PSTATE_P0:  tx_allow_rxdet = tx_p0_allow_rxdet_r;
    PSTATE_P0S: tx_allow_rxdet = tx_p0s_allow_rxdet_r;
    PSTATE_P1:  tx_allow_rxdet = tx_p1_allow_rxdet_r;
    PSTATE_P2:  tx_allow_rxdet = tx_p2_allow_rxdet_r;
  endcase
end

// TX ctl state machine states
localparam stTX_CTL_IDLE        = 3'd0;
localparam stTX_CTL_MSTATE_OFF  = 3'd1;
localparam stTX_CTL_MSTATE_ON   = 3'd2;
localparam stTX_CTL_PMA_ACK     = 3'd3;
localparam stTX_CTL_PCS_ACK     = 3'd4;
localparam stTX_CTL_MPLL_OFF    = 3'd5;

// --------------------------------------------------
// TX ctl state machine
// --------------------------------------------------
always @ (posedge cr_clk or posedge cr_tx_rst) begin
  if (cr_tx_rst) begin
    tx_ctl_state       <= stTX_CTL_IDLE;
    tx_ctl_reset       <= 1'b1;
    req_tx_pstate      <= PSTATE_P2;
    req_tx_lpd         <= 1'b0;
    req_tx_width       <= 2'b11;
    req_tx_rate        <= 3'b010;
    req_tx_mpllb_sel   <= 1'b0;
    req_tx_mpll_en     <= 1'b0;
    tx_ack_i           <= 1'b1;
    tx_detrx_result_i  <= 1'b0;
    tx_mplla_en        <= 1'b0;
    tx_mpllb_en        <= 1'b0;
    tx_pma_req_i       <= 1'b0;
    tx_pma_detrx_req_i <= 1'b0;
    tx_wait_cnt        <= 9'd0;
    curr_tx_pstate     <= PSTATE_P2;
    curr_tx_lpd        <= 1'b0;
    curr_tx_width      <= 2'b11;
    curr_tx_rate       <= 3'b010;
    curr_tx_mpllb_sel  <= 1'b0;
    curr_tx_mpll_en    <= 1'b0;
  end else begin
    case (tx_ctl_state)
      stTX_CTL_IDLE: begin
        // RX request (or coming out of reset)
        if (new_req) begin
          // hand instantiated mux outputs
          req_tx_pstate <= req_tx_pstate_nxt;
          req_tx_lpd <= req_tx_lpd_nxt;
          req_tx_width <= req_tx_width_nxt;
          req_tx_rate <= req_tx_rate_nxt;
          req_tx_mpllb_sel <= req_tx_mpllb_sel_nxt;
          req_tx_mpll_en <= req_tx_mpll_en_nxt;

          // -------------------------------------------------------------
          // Check request (use *_nxt value in this state)
          // -------------------------------------------------------------
            
          // if requested mpll_en=1 (or rate change with mpll already currently on)
          // then the current MPLL should be kept-on
          // and the requested MPLL should be turned-on
          if (req_tx_mpll_en_nxt | (req_rate_change & curr_tx_mpll_en)) begin
            // check first if selected MPLL is disabled but state is high
            // (should go low in a few cycles)
            if (req_tx_mpllb_sel_nxt ? (~mpllb_en_i & mpllb_state_crclk) :
                                       (~mplla_en_i & mplla_state_crclk))
              tx_ctl_state <= stTX_CTL_MSTATE_OFF; // wait for state to deassert
            else begin
              // the appropriate MPLL should be enabled by this lane
              // (but don't yet disable the other MPLL if it is enabled)
              if (req_tx_mpllb_sel_nxt) tx_mpllb_en <= 1'b1;
              else tx_mplla_en <= 1'b1;
            
              // if required local mpll is already on (i.e. en=state=1) and the master mpll is on, then
              // request is directly sent to pma in this state, so go to ack state next
              if (req_tx_mpllb_sel_nxt ? (mpllb_en_i & mpllb_state_crclk & master_mpllb_state_crclk) :
                                         (mplla_en_i & mplla_state_crclk & master_mplla_state_crclk))
                tx_ctl_state <= stTX_CTL_PMA_ACK;
              // otherwise wait for the local mpll (and master mpll) to turn on before sending request to pma
              else
                tx_ctl_state <= stTX_CTL_MSTATE_ON;
            end

            // Special cases:
            //
            // 1) powering up MPLL and also transitioning TX lane P2->P1
            // both these can be done in parallel (mpll pwrup and cmn mode pwrup)
            // So, send P2->P1 request to PMA lane TX while also powering up MPLL
            //
            // 2) if required local mpll is already on (i.e. en=state=1) and master
            // mpll is already on, then directly send request to pma
            if ((curr_tx_pstate == PSTATE_P2 && req_tx_pstate_nxt == PSTATE_P1) |
                (req_tx_mpllb_sel_nxt ? (mpllb_en_i & mpllb_state_crclk & master_mpllb_state_crclk) :
                                        (mplla_en_i & mplla_state_crclk & master_mplla_state_crclk))) begin
              tx_pma_req_i <= 1'b1;
            end
          end

          // for any other request, send directly to PMA lane TX
          // then wait for ack
          else begin
            tx_pma_req_i <= 1'b1;
            tx_ctl_state <= stTX_CTL_PMA_ACK;
          end
        end
        // RX detect request
        else if (tx_detrx_req_crclk && tx_allow_rxdet) begin
          tx_ctl_state <= stTX_CTL_PMA_ACK;
          tx_pma_detrx_req_i <= 1'b1;
        end
      end
         
      stTX_CTL_MSTATE_OFF: begin
        // wait for mpll_state to deassert
        if (req_tx_mpllb_sel ? ~mpllb_state_crclk : ~mplla_state_crclk) begin
          // required MPLL state is now deasserted, so assert its enable
          // (but don't yet disable the other MPLL if it is enabled)
          if (req_tx_mpllb_sel) tx_mpllb_en <= 1'b1;
          else tx_mplla_en <= 1'b1;
          tx_ctl_state <= stTX_CTL_MSTATE_ON;
        end
      end
         
      stTX_CTL_MSTATE_ON: begin
        // wait for both local mpll_state and master_mpll_state to assert
        if (req_tx_mpllb_sel ? (mpllb_state_crclk & master_mpllb_state_crclk)
                             : (mplla_state_crclk & master_mplla_state_crclk)) begin
          // required MPLLs are now on, so send request to PMA lane TX
          // then wait for ack
          tx_pma_req_i <= 1'b1;
          tx_ctl_state <= stTX_CTL_PMA_ACK;
        end
      end
         
      stTX_CTL_PMA_ACK: begin
        // wait for PMA lane TX ack
        if (tx_pma_ack_crclk) begin
          // deassert request to PMA lane TX
          tx_pma_req_i <= 1'b0;
          tx_pma_detrx_req_i <= 1'b0;
          // assert ack to PCS
          tx_ack_i <= 1'b1;
          // update detrx result on (tx_pma_detrx_req & tx_pma_ack_crclk)
          tx_detrx_result_i <= tx_pma_detrx_result_nxt; // hand instantiated mux output
          tx_ctl_state <= stTX_CTL_PCS_ACK;
        end
      end
         
      stTX_CTL_PCS_ACK: begin
        // wait for both PCS requests and PMA ack to deassert
        // (note: if coming out of reset, then there is no request)
        if (((~tx_req_crclk & ~tx_detrx_req_crclk) | tx_ctl_reset) & ~tx_pma_ack_crclk) begin
          tx_ctl_reset <= 1'b0; // release reset
          // turn off the unselected MPLL if mpll_en=1, and both if mpll_en=0
          tx_mplla_en <= ~req_tx_mpllb_sel & req_tx_mpll_en;
          tx_mpllb_en <=  req_tx_mpllb_sel & req_tx_mpll_en;
          // if we are deasserting mpll_en, then:
          // wait for MPLL to completely turn-off (ana clocks off and FSM in IDLE)
          if (~req_tx_mpll_en & curr_tx_mpll_en) begin
            tx_wait_cnt <= {1'b0,tx_wait_mpll_off_cr_cyc[7:0]};
            tx_ctl_state <= stTX_CTL_MPLL_OFF;
          end
          // else, deassert ack and return to IDLE
          else begin
            tx_ack_i <= 1'b0;
            curr_tx_pstate    <= req_tx_pstate;    // update current pstate
            curr_tx_lpd       <= req_tx_lpd;       // update current lpd
            curr_tx_width     <= req_tx_width;     // update current width
            curr_tx_rate      <= req_tx_rate;      // update current rate
            curr_tx_mpllb_sel <= req_tx_mpllb_sel; // update current mpllb_sel
            curr_tx_mpll_en   <= req_tx_mpll_en;   // update current mpll_en
            tx_ctl_state <= stTX_CTL_IDLE;
          end
        end
      end
         
      stTX_CTL_MPLL_OFF: begin
        // wait for MPLL to completely turn-off (ana clocks off and FSM in IDLE)
        if (tx_wait_cnt_ne_0)
          tx_wait_cnt <= tx_wait_cnt_dec;
        else begin
          // deassert ack and return to IDLE
          tx_ack_i <= 1'b0;
          curr_tx_pstate    <= req_tx_pstate;    // update current pstate
          curr_tx_lpd       <= req_tx_lpd;       // update current lpd
          curr_tx_width     <= req_tx_width;     // update current width
          curr_tx_rate      <= req_tx_rate;      // update current rate
          curr_tx_mpllb_sel <= req_tx_mpllb_sel; // update current mpllb_sel
          curr_tx_mpll_en   <= req_tx_mpll_en;   // update current mpll_en
          tx_ctl_state <= stTX_CTL_IDLE;
        end
      end
      
      default: begin
        tx_ctl_state <= stTX_CTL_IDLE;
      end
    endcase
  end
end

// ------------------------------------------------------------
// Need to apply delay to match powerdown delay of MPLL
// Scale delay based on ref_range.
// ------------------------------------------------------------
always @* begin
  // Note: cr_clk is divided by 2 for ref_range[2]=1
  casex(ref_range[2:0])
    3'd0: tx_wait_mpll_off_cr_cyc = {2'b00,tx_wait_mpll_off_time_r[5:0]};       // x1
    3'd1: tx_wait_mpll_off_cr_cyc = { 1'b0,tx_wait_mpll_off_time_r[5:0],1'b0};  // x2
    3'd2,
    3'd4,
    3'd5: tx_wait_mpll_off_cr_cyc = { 1'b0,tx_wait_mpll_off_time_r[5:0],1'b0} + {2'b00,tx_wait_mpll_off_time_r[5:0]}; // x3
    3'd3,
    3'd6,
    3'd7: tx_wait_mpll_off_cr_cyc = {      tx_wait_mpll_off_time_r[5:0],2'b00}; // x4
  endcase
end


// ------------------------------------------------------------
// Select clock source for tx_clk
// ------------------------------------------------------------
wire [14:0] tx_clk_in;
wire [14:0] tx_clk_in_rst;
wire [14:0] tx_clk_in_en;
wire [15:0] tx_clk_in_gate;

// Clock sources
// Mantis 6761 - Updated tx_clk_i muxing to reduce number of muxing
// stages on functional clock.  Skip performing any sync/gating
// on tx_clk_in[0] and set it to 1'b0.
assign tx_clk_in[0]  = 1'b0;
assign tx_clk_in[1]  = mplla_word_clk_i;
assign tx_clk_in[2]  = mplla_dword_clk_i;
assign tx_clk_in[3]  = mplla_qword_clk_i;
assign tx_clk_in[4]  = mplla_oword_clk_i;
assign tx_clk_in[5]  = mplla_div16p5_clk_i;
assign tx_clk_in[6]  = mplla_div33_clk_i;
assign tx_clk_in[7]  = mplla_div66_clk_i;
assign tx_clk_in[8]  = mplla_div_clk_i;
assign tx_clk_in[9]  = mpllb_word_clk_i;
assign tx_clk_in[10] = mpllb_dword_clk_i;
assign tx_clk_in[11] = mpllb_qword_clk_i;
assign tx_clk_in[12] = mpllb_oword_clk_i;
assign tx_clk_in[13] = mpllb_div_clk_i;
assign tx_clk_in[14] = rx_clk_i;

wire cr_rst_int;
dwc_e12mp_phy_x4_ns_gen_mux cr_rst_scan_mux (
  .out (cr_rst_int),
  .sel (scan_mode_i),
  .d0  (cr_rst),
  .d1  (tx_clk_en_r)
);

// Gate each clock source with a sync'ed enable
genvar i;
generate for (i=1; i<15; i=i+1) begin: gate_tx_clk_in
  dwc_e12mp_phy_x4_ns_pcs_raw_gen_rst_sync clk_in_rst_sync (
    .sync_rst        (tx_clk_in_rst[i]),
    .clk             (tx_clk_in[i]),
    .async_rst       (cr_rst_int),
    .scan_mode_i     (scan_mode_i),
    .scan_set_rst_i  (scan_set_rst_i)
  );
  
  // sync clock enable
  dwc_e12mp_phy_x4_ns_gen_sync clk_in_en_sync (
    .q   (tx_clk_in_en[i]),
    .clk (tx_clk_in[i]),
    .rst (tx_clk_in_rst[i]),
    .d   (tx_clk_en_r)
  );
  
  // clock gating
  dwc_e12mp_phy_x4_ns_gen_clk_gate clk_in_gate (
    .en_clk          (tx_clk_in_gate[i]),
    .clk             (tx_clk_in[i]),
    .rst             (tx_clk_in_rst[i]),
    .en              (tx_clk_in_en[i]),
    .scan_mode_i     (scan_mode_i),
    .scan_shift_cg_i (scan_shift_cg_i),
    .scan_set_rst_i  (scan_set_rst_i)
  );
end
endgenerate

// Connect selected clock to tx_pma_clk_i
// use hand-instantiated muxes
//always @*
//begin
//   case (tx_clk_sel_r)
//     4'd0  : tx_pma_clk_i = tx_clk_in_gate[0];
//     4'd1  : tx_pma_clk_i = tx_clk_in_gate[1];
//     4'd2  : tx_pma_clk_i = tx_clk_in_gate[2];
//     4'd3  : tx_pma_clk_i = tx_clk_in_gate[3];
//     4'd4  : tx_pma_clk_i = tx_clk_in_gate[4];
//     4'd5  : tx_pma_clk_i = tx_clk_in_gate[5];
//     4'd6  : tx_pma_clk_i = tx_clk_in_gate[6];
//     4'd7  : tx_pma_clk_i = tx_clk_in_gate[7];
//     4'd8  : tx_pma_clk_i = tx_clk_in_gate[8];
//     4'd9  : tx_pma_clk_i = tx_clk_in_gate[9];
//     4'd10 : tx_pma_clk_i = tx_clk_in_gate[10];
//     4'd11 : tx_pma_clk_i = tx_clk_in_gate[11];
//     4'd12 : tx_pma_clk_i = tx_clk_in_gate[12];
//     4'd13 : tx_pma_clk_i = tx_clk_in_gate[13];
//     4'd14 : tx_pma_clk_i = tx_clk_in_gate[14];
//     default:tx_pma_clk_i = tx_clk_in_gate[0];
//   endcase
//end

// Mantis 6761 - Updated tx_clk_i muxing to reduce number of muxing
// stages on functional clock.  We only have 15 clock inputs, so
// set unused clock selections to 0 as well as tx_clk_in_gate[0]
// since tx_clk_i will be muxed in last.
assign tx_clk_in_gate[0] = 1'b0; 
assign tx_clk_in_gate[15] = 1'b0; 

// Select based on tx_clk_sel_r[0]
wire [7:0] tx_clk_in_gate_mux0;
generate for (i=0; i<8; i=i+1) begin: sel_tx_clk_in_mux0
  dwc_e12mp_phy_x4_ns_gen_clk_mux tx_clk_sel_mux0 (
    .out (tx_clk_in_gate_mux0[i]),
    .sel (tx_clk_sel_r[0]),
    .d0  (tx_clk_in_gate[2*i]),
    .d1  (tx_clk_in_gate[2*i+1])
  );
end
endgenerate

// Select based on tx_clk_sel_r[1]
wire [3:0] tx_clk_in_gate_mux1;
generate for (i=0; i<4; i=i+1) begin: sel_tx_clk_in_mux1
  dwc_e12mp_phy_x4_ns_gen_clk_mux tx_clk_sel_mux1 (
    .out (tx_clk_in_gate_mux1[i]),
    .sel (tx_clk_sel_r[1]),
    .d0  (tx_clk_in_gate_mux0[2*i]),
    .d1  (tx_clk_in_gate_mux0[2*i+1])
  );
end
endgenerate

// Select based on tx_clk_sel_r[2]
wire [1:0] tx_clk_in_gate_mux2;
generate for (i=0; i<2; i=i+1) begin: sel_tx_clk_in_mux2
  dwc_e12mp_phy_x4_ns_gen_clk_mux tx_clk_sel_mux2 (
    .out (tx_clk_in_gate_mux2[i]),
    .sel (tx_clk_sel_r[2]),
    .d0  (tx_clk_in_gate_mux1[2*i]),
    .d1  (tx_clk_in_gate_mux1[2*i+1])
  );
end
endgenerate

// Select based on tx_clk_sel_r[3]
wire tx_clk_in_gate_mux3;
dwc_e12mp_phy_x4_ns_gen_clk_mux tx_clk_sel_mux3 (
  .out (tx_clk_in_gate_mux3),
  .sel (tx_clk_sel_r[3]),
  .d0  (tx_clk_in_gate_mux2[0]),
  .d1  (tx_clk_in_gate_mux2[1])
);

// Mantis 6761 - Updated tx_clk_i muxing to reduce number of muxing
// stages on functional clock
wire sel_func_clk;
dwc_e12mp_phy_x4_ns_pcs_raw_gen_clk_or2 sel_func_clk_or (
  .out  (sel_func_clk), 
  .clk1 (scan_mode_i), 
  .clk2 ((tx_clk_sel_r == 4'd0))
);

// Mux in scan_clk with CR clock 
// Mantis 6554: tx_pma_clk_i is not controllable in scan mode
//assign tx_pma_clk_i = tx_clk_in_gate_mux3;
dwc_e12mp_phy_x4_ns_gen_clk_mux tx_pma_clk_scan_mux (
  .out (tx_pma_clk_i),
  .sel (sel_func_clk),
  .d0  (tx_clk_in_gate_mux3),
  .d1  (tx_clk_i)
);

// -------------------------------------------------------------------------
// %%CREG TX_FSM_CTL - TX FSM Control
//   OFFSET: 0x0
//   ATTR: READ,WRITE
//   FIELDS:
//      [9] TX_P0_ALLOW_RXDET      - If asserted, then rxdet request is allowed in P0
//          RESET: 0x0
//      [8] TX_P0S_ALLOW_RXDET     - If asserted, then rxdet request is allowed in P0S
//          RESET: 0x0
//      [7] TX_P1_ALLOW_RXDET      - If asserted, then rxdet request is allowed in P1
//          RESET: 0x1
//      [6] TX_P2_ALLOW_RXDET      - If asserted, then rxdet request is allowed in P2
//          RESET: 0x1
//    [5:0] TX_WAIT_MPLL_OFF_TIME - Number of ref_range cycles to wait for MPLL to turn off
//          NOTES:                  (When entering P2).
//          RESET: 0x1E
// ---------------------------------------------------------------------
wire [15:0] cr_tx_fsm_ctl_rd_data;
dwc_e12mp_phy_x4_ns_cr_rw_reg #(.WIDTH(10), .RST_VAL(10'b0011_011110), .MSK_VAL(10'd0))
 tx_fsm_ctl_reg (
  .cr_val      ({
                 tx_p0_allow_rxdet_r,
                 tx_p0s_allow_rxdet_r,
                 tx_p1_allow_rxdet_r,
                 tx_p2_allow_rxdet_r,
                 tx_wait_mpll_off_time_r[5:0]
                }),
  .cr_rd_data  (cr_tx_fsm_ctl_rd_data),
  .cr_wr_data  (cr_wr_data),
  .cr_wr_en    (cr_wr_en),
  .cr_clk      (cr_clk),
  .cr_rst      (cr_rst),
  .cr_sel      (cr_sel[0]),
  .cr_self_clr (1'b0)
);

// -------------------------------------------------------------------------
// %%CREG TX_CLK_CTL - Select clock to act as TX input clock
//   OFFSET: 0x1
//   ATTR: READ,WRITE
//   FIELDS:
//    [4:1] TX_CLK_SEL   - Select clock source for tx_pma_clk
//          NOTES:         0  - tx_pcs_clk (input clock from pcs)
//                         1  - mplla_word_clk
//                         2  - mplla_dword_clk
//                         3  - mplla_qword_clk
//                         4  - mplla_oword_clk
//                         5  - mplla_div16p5_clk
//                         6  - mplla_div33_clk
//                         7  - mplla_div66_clk
//                         8  - mplla_div_clk
//                         9  - mpllb_word_clk
//                         10 - mpllb_dword_clk
//                         11 - mpllb_qword_clk
//                         12 - mpllb_oword_clk
//                         13 - mpllb_div_clk
//                         14 - rx_clk (from pma)
//          RESET: 0x0
//      [0] TX_CLK_EN    - Enable the tx_clk to pma tx lane
//          NOTES:         TX_CLK_EN must be deasserted when switching TX_CLK_SEL
//          RESET: 0x1
// ---------------------------------------------------------------------
wire [15:0] cr_tx_clk_ctl_rd_data;
dwc_e12mp_phy_x4_ns_cr_rw_reg #(.WIDTH(5), .RST_VAL(5'b0000_1), .MSK_VAL(5'd0))
 tx_clk_ctl_reg (
  .cr_val      ({
                 tx_clk_sel_r[3:0],
                 tx_clk_en_r
                }),
  .cr_rd_data  (cr_tx_clk_ctl_rd_data),
  .cr_wr_data  (cr_wr_data),
  .cr_wr_en    (cr_wr_en),
  .cr_clk      (cr_clk),
  .cr_rst      (cr_rst),
  .cr_sel      (cr_sel[1]),
  .cr_self_clr (1'b0)
);


// Muxed register read for CREG register
//     
assign cr_rd_data = cr_tx_fsm_ctl_rd_data |
                    cr_tx_clk_ctl_rd_data;
   
// ASCII decode of state for debug
// synopsys translate_off
reg [55:0] tx_ctl_state_asc;
always @ (tx_ctl_state)
begin
   case (tx_ctl_state)
     stTX_CTL_IDLE        : tx_ctl_state_asc = "IDLE   ";
     stTX_CTL_MSTATE_OFF  : tx_ctl_state_asc = "MS_OFF ";
     stTX_CTL_MSTATE_ON   : tx_ctl_state_asc = "MS_ON  ";
     stTX_CTL_PMA_ACK     : tx_ctl_state_asc = "PMA_ACK";
     stTX_CTL_PCS_ACK     : tx_ctl_state_asc = "PCS_ACK";
     stTX_CTL_MPLL_OFF    : tx_ctl_state_asc = "MPLLOFF";
     default:             tx_ctl_state_asc = "ERR ";
   endcase
end
// synopsys translate_on

endmodule

